/**
 * Copyright &copy; 2015-2020 <a href="http://www.jeeplus.org/">JeePlus</a> All rights reserved.
 */
package com.mb.microservice.framework.utils;

import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.RandomStringUtils;
import org.apache.commons.lang3.StringUtils;
import org.apache.commons.lang3.time.DateFormatUtils;
import org.apache.commons.net.ntp.NTPUDPClient;
import org.apache.commons.net.ntp.TimeInfo;
import org.apache.commons.net.ntp.TimeStamp;

import java.io.IOException;
import java.net.InetAddress;
import java.net.UnknownHostException;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.time.Instant;
import java.time.LocalDateTime;
import java.time.ZoneOffset;
import java.time.format.DateTimeFormatter;
import java.util.Calendar;
import java.util.Date;
import java.util.Locale;

/**
 * 日期工具类, 继承org.apache.commons.lang.time.DateUtils类
 *
 * @author ChenBingHao
 */
@Slf4j
public class DateUtils extends org.apache.commons.lang3.time.DateUtils {
    

    
    private static String[] parsePatterns = { "yyyy-MM-dd", "yyyy-MM-dd HH:mm:ss", "yyyy-MM-dd HH:mm", "yyyy-MM",
            "yyyy/MM/dd", "yyyy/MM/dd HH:mm:ss", "yyyy/MM/dd HH:mm", "yyyy/MM", "yyyy.MM.dd", "yyyy.MM.dd HH:mm:ss",
            "yyyy.MM.dd HH:mm", "yyyy.MM" };
    
    public static final String PATTERN_YYYYMMDDHHMMSSSSS = "yyyyMMddHHmmssSSS";
    
    //东八区
    private static final int CHINA_ZONE_TIME = 8;
    private static final DateTimeFormatter FORMATTER = DateTimeFormatter.ofPattern(parsePatterns[1], Locale.CHINA);
    private static final ZoneOffset zoneOffset = ZoneOffset.ofHours(CHINA_ZONE_TIME);
    
    
    /**
     * 得到当前日期字符串 格式（yyyy-MM-dd）
     */
    public static String getDate() {
        return getDate("yyyy-MM-dd");
    }
    
    /**
     * 得到当前日期字符串 格式（yyyy-MM-dd） pattern可以为："yyyy-MM-dd" "HH:mm:ss" "E"
     */
    public static String getDate(String pattern) {
        return DateFormatUtils.format(new Date(), pattern);
    }
    
    /**
     * 得到日期字符串 默认格式（yyyy-MM-dd） pattern可以为："yyyy-MM-dd" "HH:mm:ss" "E"
     */
    public static String formatDate(Date date, Object... pattern) {
        String formatDate = null;
        if (pattern != null && pattern.length > 0) {
            formatDate = DateFormatUtils.format(date, pattern[0].toString());
        } else {
            formatDate = DateFormatUtils.format(date, "yyyy-MM-dd");
        }
        return formatDate;
    }
    
    /**
     * 得到日期时间字符串，转换格式（yyyy-MM-dd HH:mm:ss）
     */
    public static String formatDateTime(Date date) {
        return formatDate(date, "yyyy-MM-dd HH:mm:ss");
    }
    
    /**
     * 得到日期时间字符串，转换格式（pattern） "yyyy.MM.dd G 'at' HH:mm:ss z" 2001.07.04 AD at
     * 12:08:56 PDT "EEE, MMM d, ''yy" Wed, Jul 4, '01 "h:mm a" 12:08 PM "hh
     * 'o''clock' a, zzzz" 12 o'clock PM, Pacific Daylight Time "K:mm a, z" 0:08
     * PM, PDT "yyyyy.MMMMM.dd GGG hh:mm aaa" 02001.July.04 AD 12:08 PM "EEE, d
     * MMM yyyy HH:mm:ss Z" Wed, 4 Jul 2001 12:08:56 -0700 "yyMMddHHmmssZ"
     * 010704120856-0700 "yyyy-MM-dd'T'HH:mm:ss.SSSZ"
     * 2001-07-04T12:08:56.235-0700
     */
    public static String formatDateTime(Date date, String pattern) {
        if (date == null) {
            return "";
        }
        SimpleDateFormat sdf = new SimpleDateFormat(pattern, Locale.ENGLISH);
        return sdf.format(date);
    }
    
    /**
     * 得到当前时间字符串 格式（HH:mm:ss）
     */
    public static String getTime() {
        return formatDate(new Date(), "HH:mm:ss");
    }
    
    /**
     * 得到当前日期和时间字符串 格式（yyyy-MM-dd HH:mm:ss）
     */
    public static String getDateTime() {
        return formatDate(new Date(), "yyyy-MM-dd HH:mm:ss");
    }
    
    /**
     * 得到当前年份字符串 格式（yyyy）
     */
    public static String getYear() {
        return formatDate(new Date(), "yyyy");
    }
    
    /**
     * 得到当前月份字符串 格式（MM）
     */
    public static String getMonth() {
        return formatDate(new Date(), "MM");
    }
    
    /**
     * 得到当天字符串 格式（dd）
     */
    public static String getDay() {
        return formatDate(new Date(), "dd");
    }
    
    /**
     * 得到当前星期字符串 格式（E）星期几
     */
    public static String getWeek() {
        
        return formatDate(new Date(), "E");
    }
    
    /**
     * 日期型字符串转化为日期 格式 { "yyyy-MM-dd", "yyyy-MM-dd HH:mm:ss", "yyyy-MM-dd HH:mm",
     * "yyyy/MM/dd", "yyyy/MM/dd HH:mm:ss", "yyyy/MM/dd HH:mm", "yyyy.MM.dd",
     * "yyyy.MM.dd HH:mm:ss", "yyyy.MM.dd HH:mm" }
     */
    public static Date parseDate(Object str) {
        if (str == null) {
            return null;
        }
        try {
            return parseDate(str.toString(), parsePatterns);
        } catch (ParseException e) {
            return null;
        }
    }
    
    /**
     * 获取过去的天数
     *
     * @param date
     * @return
     */
    public static long pastDays(Date date) {
        long t = System.currentTimeMillis() - date.getTime();
        return t / (24 * 60 * 60 * 1000);
    }
    
    /**
     * 获取过去的小时
     *
     * @param date
     * @return
     */
    public static long pastHour(Date date) {
        long t = System.currentTimeMillis() - date.getTime();
        return t / (60 * 60 * 1000);
    }
    
    /**
     * 获取过去的分钟
     *
     * @param date
     * @return
     */
    public static long pastMinutes(Date date) {
        long t = System.currentTimeMillis() - date.getTime();
        return t / (60 * 1000);
    }
    
    /**
     * 转换为时间（天,时:分:秒.毫秒）
     *
     * @param timeMillis
     * @return
     */
    public static String formatDateTime(long timeMillis) {
        long day = timeMillis / (24 * 60 * 60 * 1000);
        long hour = (timeMillis / (60 * 60 * 1000) - day * 24);
        long min = ((timeMillis / (60 * 1000)) - day * 24 * 60 - hour * 60);
        long s = (timeMillis / 1000 - day * 24 * 60 * 60 - hour * 60 * 60 - min * 60);
        long sss = (timeMillis - day * 24 * 60 * 60 * 1000 - hour * 60 * 60 * 1000 - min * 60 * 1000 - s * 1000);
        return (day > 0 ? day + "," : "") + hour + ":" + min + ":" + s + "." + sss;
    }
    
    /**
     * 获取两个日期之间的天数
     *
     * @param before
     * @param after
     * @return
     */
    public static double getDistanceOfTwoDate(Date before, Date after) {
        long beforeTime = before.getTime();
        long afterTime = after.getTime();
        return (afterTime - beforeTime) / (1000 * 60 * 60 * 24);
    }
    
    /**
     * 获取两个日期之间的差异
     *
     * @param before
     * @param after
     * @return
     */
    public static long getDistanceMillisOfTwoDate(Date before, Date after) {
        long beforeTime = before.getTime();
        long afterTime = after.getTime();
        return (afterTime - beforeTime);
    }
    
    /**
     * 获取两个日期之间的差异
     *
     * @param base
     * @param dateTime
     * @return
     */
    public static Date addDateTime(Date base, long dateTime) {
        long baseTime = base.getTime();
        long resultTime = baseTime + dateTime;
        return new Date(resultTime);
    }
    
    /****
     * 传入具体日期 ，返回具体日期减一个月。
     *
     * @param date
     *            日期(2014-04-20)
     * @param incOrDecMark
     *            减为-1，增为1
     * @return 2014-03-20
     *
     * @throws ParseException
     */
    public static String addMonth(String date, int incOrDecMark) throws ParseException {
        SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd");
        Date dt = sdf.parse(date);
        return addMonth(dt, incOrDecMark, "yyyy-MM-dd");
    }
    
    public static String addMonth(Date date, int incOrDecMark, String pattern){
        SimpleDateFormat sdf = new SimpleDateFormat(pattern);
        Calendar rightNow = Calendar.getInstance();
        rightNow.setTime(date);
        
        rightNow.add(Calendar.MONTH, incOrDecMark); // 减为-1，增为1
        Date dt1 = rightNow.getTime();
        String reStr = sdf.format(dt1);
        
        return reStr;
    }
    
    /****
     * 传入具体日期 ，返回具体日期减一天。
     *
     * @param date
     *            日期(2014-04-20)
     * @param incOrDecMark
     *            减为-1，增为1
     * @return 2014-03-20
     *
     * @throws ParseException
     */
    public static String addDay(String date, int incOrDecMark) {
        return addDay(date, incOrDecMark, "yyyy-MM-dd");
    }
    
    public static String addDay(String date, int incOrDecMark, String pattern) {
        SimpleDateFormat sdf = new SimpleDateFormat(pattern);
        Date dt = null;
        try {
            dt = sdf.parse(date);
        } catch (ParseException e) {
            e.printStackTrace();
        }
        Calendar rightNow = Calendar.getInstance();
        rightNow.setTime(dt);
        
        rightNow.add(Calendar.DAY_OF_YEAR, incOrDecMark); // 减为-1，增为1
        Date dt1 = rightNow.getTime();
        String reStr = sdf.format(dt1);
        
        return reStr;
    }
    
    /****
     * 传入具体日期 ，返回具体日期减小时。
     *
     * @param date
     *            日期(2014-04-20)
     * @param incOrDecMark
     *            减为-1，增为1
     * @return 2014-03-20
     *
     * @throws ParseException
     */
    public static String addHour(String date, int incOrDecMark) throws ParseException {
        SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
        Date dt = sdf.parse(date);
        Calendar rightNow = Calendar.getInstance();
        rightNow.setTime(dt);
        
        rightNow.add(Calendar.HOUR, incOrDecMark); // 减为-1，增为1
        Date dt1 = rightNow.getTime();
        String reStr = sdf.format(dt1);
        
        return reStr;
    }
    
    /****
     * 获取月末最后一天
     *
     * @param sDate
     *            2014-11-24
     * @return 30
     */
    private static String getMonthMaxDay(String sDate) {
        SimpleDateFormat sdf_full = new SimpleDateFormat("yyyy-MM-dd");
        Calendar cal = Calendar.getInstance();
        Date date = null;
        try {
            date = sdf_full.parse(sDate + "-01");
        } catch (ParseException e) {
            e.printStackTrace();
        }
        cal.setTime(date);
        int last = cal.getActualMaximum(Calendar.DATE);
        return String.valueOf(last);
    }
    
    // 判断是否是月末
    public static boolean isMonthEnd(Date date) {
        Calendar cal = Calendar.getInstance();
        cal.setTime(date);
        if (cal.get(Calendar.DATE) == cal.getActualMaximum(Calendar.DAY_OF_MONTH)) {
            return true;
        }
        return false;
    }
    
    /***
     * 日期减一天、加一天
     *
     * @param option
     *            传入类型 pro：日期减一天，next：日期加一天
     * @param _date
     *            2014-11-24
     * @return 减一天：2014-11-23或(加一天：2014-11-25)
     */
    public static String checkOption(String option, String _date) {
        SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd");
        Calendar cl = Calendar.getInstance();
        Date date = null;
        
        try {
            date = (Date) sdf.parse(_date);
        } catch (ParseException e) {
            e.printStackTrace();
        }
        cl.setTime(date);
        if ("pre".equals(option)) {
            // 时间减一天
            cl.add(Calendar.DAY_OF_MONTH, -1);
            
        } else if ("next".equals(option)) {
            // 时间加一天
            cl.add(Calendar.DAY_OF_YEAR, 1);
        } else {
            // do nothing
        }
        date = cl.getTime();
        return sdf.format(date);
    }
    
    /***
     * 判断日期是否为当前月， 是当前月返回当月最小日期和当月目前最大日期以及传入日期上月的最大日和最小日
     * 不是当前月返回传入月份的最大日和最小日以及传入日期上月的最大日和最小日
     *
     * @param date
     *            日期 例如：2014-11
     * @return String[] 开始日期，结束日期，上月开始日期，上月结束日期
     * @throws ParseException
     */
    public static String[] getNow_Pre_Date(String date) throws ParseException {
        
        String[] str_date = new String[4];
        Date now = new Date();
        SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM");
        SimpleDateFormat sdf_full = new SimpleDateFormat("yyyy-MM-dd");
        String stMonth = sdf.format(now);
        String stdate = "";// 开始日期
        String endate = "";// 结束日期
        String preDate_start = "";// 上月开始日期
        String preDate_end = "";// 上月结束日期
        
        // 当前月
        if (date.equals(stMonth)) {
            stdate = stMonth + "-01"; // 2014-11-01
            endate = sdf_full.format(now);// 2014-11-24
            preDate_start = addMonth(stdate, -1);// 2014-10-01
            preDate_end = addMonth(endate, -1);// 2014-10-24
        } else {
            // 非当前月
            String monthMaxDay = getMonthMaxDay(date);
            stdate = date + "-01";// 2014-10-01
            endate = date + "-" + monthMaxDay;// 2014-10-31
            preDate_start = addMonth(stdate, -1);// 2014-09-01
            preDate_end = addMonth(endate, -1);// 2014-09-30
        }
        str_date[0] = stdate;
        str_date[1] = endate;
        str_date[2] = preDate_start;
        str_date[3] = preDate_end;
        
        return str_date;
    }
    
    // 两个字符串日期对比大小
    public static int compareDate(String d1, String d2) throws Exception {
        SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd");
        if ((d1 != null) && (!d1.equals("")) && (d2 != null) && (!d2.equals(""))) {
            Date dt1 = null;
            Date dt2 = null;
            try {
                dt1 = sdf.parse(d1);
                dt2 = sdf.parse(d2);
            } catch (Exception e) {
                throw new Exception("输入的字符串日期格式不正确，请检查");
            }
            if (dt1.getTime() > dt2.getTime())
                return 1;
            if (dt1.getTime() < dt2.getTime()) {
                return -1;
            }
            return 0;
        }
        
        return 2;
    }
    
    
    /**
     * 格式化输出yyyyMMddHHmmssSSS
     */
    public static String formartTime(Date date) {
        SimpleDateFormat formarter = new SimpleDateFormat(PATTERN_YYYYMMDDHHMMSSSSS);
        return formarter.format(date);
    }
    
    /**
     * 获取时间戳17位+4位随机数
     *
     *
     */
    public static String getDateTimeStampSN() {
        String dataTime = formartTime(new Date());
        String numeric = RandomStringUtils.randomNumeric(4);
        return dataTime + numeric;
    }
    
    /**
     * 获取网络时间
     *
     * @param hostsName 网络主机的主机名或IP
     * @return
     */
    public static Date getNetworkDate(String hostsName) {
        String timeServerUrl = "ntp.sjtu.edu.cn";
        if(StringUtils.isNotBlank(hostsName)){
            timeServerUrl = hostsName;
        }
        NTPUDPClient timeClient = null;
        Date date = null;
        try {
            timeClient = new NTPUDPClient();
            InetAddress timeServerAddress = InetAddress.getByName(timeServerUrl);
            TimeInfo timeInfo = timeClient.getTime(timeServerAddress);
            TimeStamp timeStamp = timeInfo.getMessage().getTransmitTimeStamp();
            timeClient.close();
            date = timeStamp.getDate();
        } catch (UnknownHostException e) {
            log.error("UnknownHostException: ", e);
        } catch (IOException e) {
            log.error("IOException: ", e);
        }finally {
            if(timeClient != null && timeClient.isOpen()){
                timeClient.close();
            }
        }
        return date;
    }
    
    /**
     * 获取某一天的几天之前/之后的时间
     *
     * @param date
     * @param incOrDecMark -之前，+之后
     * @param pattern
     * @return
     */
    public static String getDateOfAgoOrAfter(Date date, int incOrDecMark, String pattern){
        Calendar calendar = Calendar.getInstance();
        calendar.setTime(date);
        calendar.add(Calendar.DAY_OF_YEAR, incOrDecMark);
        return DateUtils.formatDate(calendar.getTime(),pattern);
    }
    
    /**
     * 获取某一天的几天之前/之后的时间
     *
     * @param date
     * @param incOrDecMark -之前，+之后
     * @param pattern
     * @return
     */
    public static String getDateOfAgoOrAfter(Date date, int filed, int incOrDecMark, String pattern){
        Calendar calendar = Calendar.getInstance();
        calendar.setTime(date);
        calendar.add(filed, incOrDecMark);
        return DateUtils.formatDate(calendar.getTime(),pattern);
    }
    
    /**
     * 当前季度的开始时间，即2020-07-01 00:00:00
     *
     * @return
     */
    public static String getCurrentQuarterStartDate (Date date, String pattern){
        Calendar calendar = Calendar.getInstance();
        calendar.setTime(date);
        int currentMonth = calendar.get(Calendar.MONTH) + 1;
        if (currentMonth >= 1 && currentMonth <= 3) {
            calendar.set(Calendar.MONTH, 0);
        }else if (currentMonth >= 4 && currentMonth <= 6) {
            calendar.set(Calendar.MONTH, 3);
        }else if (currentMonth >= 7 && currentMonth <= 9) {
            calendar.set(Calendar.MONTH, 4);
        }else if (currentMonth >= 10 && currentMonth <= 12) {
            calendar.set(Calendar.MONTH, 9);
        }
        calendar.set(Calendar.DATE, 1);
        return formatDate(calendar.getTime(), pattern);
    }
    
    
    public static long datestr2Timestamp(String dateStr) {
        LocalDateTime localDateTime = LocalDateTime.parse(dateStr, FORMATTER);
        long timestamp = localDateTime.toInstant(zoneOffset).toEpochMilli();
        return timestamp;
    }
    
    public static String timestamp2DateStr(long timestamp) {
        Instant instant = Instant.ofEpochMilli(timestamp);
        LocalDateTime localDateTime = LocalDateTime.ofInstant(instant, zoneOffset);
        String format = FORMATTER.format(localDateTime);
        return format;
    }
}
